package ar.edu.itba.pod.legajo47573;

import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashSet;
import java.util.Set;

import ar.edu.itba.node.Node;
import ar.edu.itba.node.NodeInformation;
import ar.edu.itba.node.api.ClusterAdministration;


public class ClusterAdministrationImpl extends UnicastRemoteObject implements ClusterAdministration{

	/**
	 * TODO Ver esto del serial
	 */
	private static final long serialVersionUID = 1847635297796530518L;
	private static final String GROUP_NAME = "inicio";
	private String groupName = null;
	private MyNode currentNode;
	private Set<NodeInformation> nodes = new HashSet<NodeInformation>();
	
	public ClusterAdministrationImpl(MyNode currentNode) throws RemoteException{
		this.currentNode = currentNode;
		Registry registry = LocateRegistry.createRegistry(currentNode.getInfo().port());
		try {
			registry.bind(Node.CLUSTER_COMUNICATION, this);
		} catch (AlreadyBoundException e) {
			e.printStackTrace();
		}
	}
	
	public Set<NodeInformation> getKnownNodes(){
		System.out.println("Soy el nodo " + currentNode.getInfo().id());
		System.out.println("Mis nodos son");
		System.out.println(nodes);
		return nodes;
	}
	
	@Override
	public void createGroup() throws RemoteException {
		System.out.println("entre");
		if(this.groupName != null)
			throw new IllegalStateException("Group already created");
		else
		{
			this.groupName = GROUP_NAME;
			System.out.println("Group " + GROUP_NAME + " created");
		}
	}

	@Override
	public String getGroupId() throws RemoteException {
		return this.groupName;
	}

	@Override
	public boolean isConnectedToGroup() throws RemoteException {
		/*Si el grupo está creado entonces el primer nodo está conectado*/
		if(groupName != null)
			return true;
		return false;
	}

	@Override
	public void connectToGroup(String host, int port) throws RemoteException, NotBoundException {
		//Aca no tengo toda la info del nodo al que me voy a conectar (host y puerto) me falta el id, entonces addNewNode
		//debería devolverme el nodo actual también
		
		/*La primera vez que me conecto creo el registry y abro mi puerto*/
		//TODO pasarlo al constructor para que quede en todas las implementaciones igual
		
		/*Pido el objeto remoto del nodo al que me voy a conectar y agrego el nodo que se quiere conectar*/
		Registry remoteRegistry = LocateRegistry.getRegistry(host, port);
		ClusterAdministration stub = (ClusterAdministration)remoteRegistry.lookup(Node.CLUSTER_COMUNICATION);
		
		System.out.println("connect");
		
		Set<NodeInformation> otherNodes = stub.addNewNode(this.currentNode.getInfo());
		
		for(NodeInformation it : otherNodes){
			//TODO sacar el if total es un Set
			//if(!nodes.contains(it))
				this.nodes.add(it);
		}
		
		getKnownNodes();
	}

	@Override
	public void disconnectFromGroup(NodeInformation node)
			throws RemoteException, NotBoundException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Set<NodeInformation> addNewNode(NodeInformation node)
			throws RemoteException, NotBoundException {
		System.out.println("addNode");
		
		/*Primero veo si está ya este nodo o si no*/
		if(nodes.contains(node))
			return null;
		else
			/*Lo agrego inmediatamente porque sino la condicion de arriba no se cumple nunca*/
			this.nodes.add(node);
		
		Set<NodeInformation> iterator = new HashSet<NodeInformation>(nodes);
		/*Elimino el nodo que recién agregué para no iterar sobre él*/
		iterator.remove(node);
		
		/*Llamo a addNode de los nodos que conozco*/
		System.out.println(nodes.size());
		for(NodeInformation it : iterator){
			Registry remoteRegistry = LocateRegistry.getRegistry(it.host(), it.port());
			ClusterAdministration stub = (ClusterAdministration)remoteRegistry.lookup(Node.CLUSTER_COMUNICATION);
			stub.addNewNode(node);
		}
		
		/*Agrego el nodo; ya estoy seguro de que no estaba entre los nodos del grupo*/
		
		Set<NodeInformation> ans = new HashSet<NodeInformation>(nodes);
		/*Agrego la info del nodo actual a la lista de nodos xq en connectToGroup no tengo
		 * el id del nodo*/
		ans.remove(node);
		ans.add(this.currentNode.getInfo());
		
		getKnownNodes();
		
		return ans;
	}

	@Override
	public Set<NodeInformation> connectedNodes() throws RemoteException {
		// TODO Auto-generated method stub
		return null;
	}

}
